约瑟夫环问题(带密码)
编号为1,2,…,n的n个人按顺时针方向围坐一圈,每个人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m 值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直到所有人全部出列为止。例如,n=7,7个人的密码依次为:3,1,7,2,4,8,4,m的初值取6,则正确的出列顺序应为6,1,4,7,2,3,5
分析
约瑟夫环的大小是变化的,因此相应的结点也是变化的,使用链式存储结构可以动态的生成其中的结点,出列操作也非常简单。用单向循环链表模拟其出列顺序比较合适
解决
用结构指针描述每个人
struct Joseph {
//约瑟夫环中某个人的序号
int number;
//约瑟夫环中某个人的密码
int password;
//约瑟夫环的下一个节点
struct Joseph *next;
};
初始化约瑟夫环
Node *init(int n, int array[]) {
int i = 1;
//链表头结点
Node *head;
//链表当前节点
Node *current;
//链表下一个节点
Node *next;
current = static_cast<Node *>(malloc(sizeof(Node)));
current->number = i;
current->password = array[i - 1];
head = current;
for (i = 2; i <= n; ++i) {
next = static_cast<Node *>(malloc(sizeof(Node)));
next->number = i;
next->password = array[i - 1];
//连接链表与新创建的节点
current->next = next;
current = next;
}
//尾节点连接到头结点,形成循环链表
current->next = head;
return head;
}
报数出列
void function(Node *head, int len, int password) {
//前一个节点
Node *pre;
//后一个节点
Node *next;
//要删除的节点
Node *temp;
//初始化
next = head;
for (int i = 1; i < len; ++i) {
pre = next->next;
next = next->next;
}
//遍历所有人
for (int i = 1; i <= len; ++i) {
//先将指针移动到出列的前一个
for (int j = 1; j < password; ++j) {
pre = pre->next;
}
//保存要删除的节点
temp = pre->next;
//下一个节点
next = temp->next;
//修改密码
password = temp->password;
//输出出列序号
cout << temp->number << endl;
//连接链表,去除中间节点
pre->next = next;
//释放中间节点
free(temp);
}
}
完整代码
#include <iostream>
using namespace std;
struct Joseph {
//约瑟夫环中某个人的序号
int number;
//约瑟夫环中某个人的密码
int password;
//约瑟夫环的下一个节点
struct Joseph *next;
};
typedef Joseph Node;
/**
* 初始化约瑟夫环
* @param n 成员数目
* @param array 每个人的密码
* @return 头结点
*/
Node *init(int n, int array[]) {
int i = 1;
//链表头结点
Node *head;
//链表当前节点
Node *current;
//链表下一个节点
Node *next;
current = static_cast<Node *>(malloc(sizeof(Node)));
current->number = i;
current->password = array[i - 1];
head = current;
for (i = 2; i <= n; ++i) {
next = static_cast<Node *>(malloc(sizeof(Node)));
next->number = i;
next->password = array[i - 1];
//连接链表与新创建的节点
current->next = next;
current = next;
}
//尾节点连接到头结点,形成循环链表
current->next = head;
return head;
}
/**
* 遍历约瑟夫环,报数出列
* @param head 头结点
* @param len 约瑟夫环长度
* @param password 初始密码
*/
void function(Node *head, int len, int password) {
//前一个节点
Node *pre;
//后一个节点
Node *next;
//要删除的节点
Node *temp;
//初始化
next = head;
for (int i = 1; i < len; ++i) {
pre = next->next;
next = next->next;
}
//遍历所有人
for (int i = 1; i <= len; ++i) {
//先将指针移动到出列的前一个
for (int j = 1; j < password; ++j) {
pre = pre->next;
}
//保存要删除的节点
temp = pre->next;
//下一个节点
next = temp->next;
//修改密码
password = temp->password;
//输出出列序号
cout << temp->number << endl;
//连接链表,去除中间节点
pre->next = next;
//释放中间节点
free(temp);
}
}
int main() {
//环的长度
int len;
cout << "输入约瑟夫环的长度:";
cin >> len;
int array[len];
cout << "请输入m的初始值 m:";
int m;
cin >> m;
cout << "请输入每个人的密码: " << endl;
for (int i = 0; i < len; ++i) {
cin >> array[i];
}
// int len = 7;
// int array[] = {3, 1, 7, 2, 4, 8, 4};
// int m = 6;
//创建约瑟夫环
Node *head = init(len, array);
function(head, len, m);
return 0;
}